package me.softwareengineer.guice.rendering.domain;

import java.util.List;

import me.softwareengineer.guice.rendering.listener.EndDocumentEvent;
import me.softwareengineer.guice.rendering.listener.Listener;
import me.softwareengineer.guice.rendering.listener.StartDocumentEvent;
import me.softwareengineer.guice.rendering.parser.Parser;
import me.softwareengineer.guice.rendering.render.Render;

/**
 * Represents a DOM (Document Object Model) tree. A DOM is a standard tree structure, 
 * where each node contains one of the elements. The two most common types of nodes are {@link HasChildren} nodes 
 * and single nodes. DOM tree is the output result of a parsing process. In addition, it is used as input
 * variable in a rendering process.
 * 
 * 
 * <pre>
 * This class is thread-safe and can be cached/re-used and used concurrently.
 * </pre>
 * 
 * @author yad.tahir
 * @version 1.0
 * @see StartDocumentEvent
 * @see EndDocumentEvent
 * @see Parser
 * @see Element
 * @see Render
 * 
 */
//This class is marked as final since we want to make immutable as much as possible
public final class DOM extends Element {

	/**
	 * Stateful id generator for this document. We store it in the XDOM because
	 * it is the only object which remains the same between parsing,
	 * transformation and rendering, and we need to generate ids during parsing
	 * and during transformation.
	 */
	private final IdGenerator idGenerator;

	/**
	 * @param childElements
	 *            the list of children elements of the element to construct
	 * @see Element#Element(List)
	 */
	public DOM(List<Element> childElements) {
		this(childElements, new IdGenerator(), null);
	}

	/**
	 * @param childBlocks
	 *            the list of children blocks of the block to construct
	 * @param metaData
	 *            the meta data to add for this block
	 * @see Element#Element(List)
	 */
	public DOM(List<Element> childBlocks, HasMetaData metaData) {
		this(childBlocks, new IdGenerator(), metaData);
	}

	/**
	 * @param childBlocks
	 *            the list of children blocks of the block to construct
	 * @param idGenerator
	 *            a stateful id generator for this document
	 */
	public DOM(List<Element> childBlocks, IdGenerator idGenerator) {
		this(childBlocks, idGenerator, null);
	}

	/**
	 * @param childBlocks
	 *            the list of children blocks of the block to construct
	 * @param metaData
	 *            the meta data to add for this block
	 * @param idGenerator
	 *            a stateful id generator for this document
	 * @see Element#Element(List)
	 */
	public DOM(List<Element> childBlocks, IdGenerator idGenerator,
			HasMetaData metaData) {
		super(childBlocks);
		
		this.idGenerator = idGenerator;
		this.addMetaData(metaData);
	}

	/**
	 * @return a stateful id generator for the whole document.
	 *  <p>
	 * This property is immutable and thread-safe
	 * </p>
	 */
	public IdGenerator getIdGenerator() {
		synchronized (idGenerator) {
			return this.idGenerator;
		}
	}
	
	/**
	 * {@inheritDoc}
	 * This method passes the following event to the provided listener: {@link EndDocumentEvent}
	 * 
	 */
	@Override
	public void afterElement(Listener listener){
		EndDocumentEvent e = new EndDocumentEvent(getAllProperties(), getAllMetaData());
		listener.onEvent(e);
	}
	
	/**
	 * {@inheritDoc}
	 * This method fires the following event: {@link StartDocumentEvent}
	 */
	public void beforeElement(Listener listener){
		StartDocumentEvent e = new StartDocumentEvent(getAllProperties(), getAllMetaData());
		listener.onEvent(e);
	}
	
}
